﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Xunit;
using MongoServices.Tests.TestHelpers;
using MongoServices.Tests.Stubs;
using Moq;

namespace MongoServices.Tests.ConfigurationTests
{
	public class BeforeAndAfterSaveProcessTests : TestBase
	{
		protected const string AFTER_BEFORE_PROCESS_STRING = "AfterBeforeProcessString";
		protected const string AFTER_AFTER_PROCESS_STRING = "AfterAfterProcessString";

		protected Mock<IReadWriteRepository<TestModelSimple>> ReadWriteRepositoryMock { get; private set; }
		protected ReadWriteService<TestModelSimple> ReadWriteServiceTestModelSimple { get; private set; }

		public BeforeAndAfterSaveProcessTests()
		{
			Setup();
		}

		private void Setup()
		{
			ReadWriteRepositoryMock = new Mock<IReadWriteRepository<TestModelSimple>>();
			ReadWriteServiceTestModelSimple = new ReadWriteServiceTestModelSimpleStub(ReadWriteRepositoryMock.Object);
		}

		public override void Dispose()
		{
			base.Dispose();

			ReadWriteService<TestModelSimple>.ClearAfterSaveProcesses();
			ReadWriteService<TestModelSimple>.ClearBeforeSaveProcesses();
		}

		public static bool BeforeSaveProcessReturningTrue(TestModelSimple model)
		{
			model.TestField = AFTER_BEFORE_PROCESS_STRING;
			return true;
		}

		public static bool BeforeSaveProcessReturningFalse(TestModelSimple model)
		{
			model.TestField = AFTER_BEFORE_PROCESS_STRING;
			return false;
		}

		public static void AfterSaveProcess(TestModelSimple model)
		{
			model.TestField = AFTER_AFTER_PROCESS_STRING;
		}
	}

	public class WhenBeforeSaveProcessIsAdded : BeforeAndAfterSaveProcessTests
	{
		[Fact]
		public void TheRepositorySaveMethodWillBeCalledWhenBeforeSaveProcessReturnsTrue()
		{
			var testModel = new TestModelSimple { TestField = "TestData" };

			ReadWriteService<TestModelSimple>.AddBeforeSaveProcess(BeforeAndAfterSaveProcessTests.BeforeSaveProcessReturningTrue);
			ReadWriteServiceTestModelSimple.Save(testModel);

			ReadWriteRepositoryMock.Verify(m => m.Save(It.IsAny<TestModelSimple>()), Times.Once());
		}

		[Fact]
		public void ASaveResultOfSavedWillBeReturnByTheSaveMethodWhenBeforeSaveProcessReturnsTrue()
		{
			var testModel = new TestModelSimple();

			ReadWriteService<TestModelSimple>.AddBeforeSaveProcess(BeforeAndAfterSaveProcessTests.BeforeSaveProcessReturningTrue);
			var response = ReadWriteServiceTestModelSimple.Save(testModel);

			Assert.Equal(SaveResult.Saved, response);
		}

		[Fact]
		public void TheRepositorySaveMethodWillBeCalledWithTheUpdatedData()
		{
			var testModel = new TestModelSimple();

			ReadWriteService<TestModelSimple>.AddBeforeSaveProcess(BeforeAndAfterSaveProcessTests.BeforeSaveProcessReturningTrue);
			ReadWriteServiceTestModelSimple.Save(testModel);

			ReadWriteRepositoryMock.Verify(m => m.Save(It.Is<TestModelSimple>(tm => tm.TestField == AFTER_BEFORE_PROCESS_STRING)), Times.Once());
		}

		[Fact]
		public void TheRepositorySaveMethodWillNotBeCalledWhenBeforeSaveProcessReturnsFalse()
		{
			var testModel = new TestModelSimple();

			ReadWriteService<TestModelSimple>.AddBeforeSaveProcess(BeforeAndAfterSaveProcessTests.BeforeSaveProcessReturningFalse);
			ReadWriteServiceTestModelSimple.Save(testModel);

			ReadWriteRepositoryMock.Verify(m => m.Save(It.IsAny<TestModelSimple>()), Times.Never());
		}

		[Fact]
		public void ASaveResultOfBeforeSaveFailureWillBeReturnByTheSaveMethodWhenBeforeSaveProcessReturnsFalse()
		{
			var testModel = new TestModelSimple();

			ReadWriteService<TestModelSimple>.AddBeforeSaveProcess(BeforeAndAfterSaveProcessTests.BeforeSaveProcessReturningFalse);
			var response = ReadWriteServiceTestModelSimple.Save(testModel);

			Assert.Equal(SaveResult.BeforeSaveFailure, response);
		}
	}

	public class WhenAfterSaveProcessIsAdded : BeforeAndAfterSaveProcessTests
	{
		[Fact]
		public void TheModelWillBeUpdatedToTheAppropriateValue()
		{
			var testModel = new TestModelSimple { TestField = "TestData" };

			ReadWriteService<TestModelSimple>.AddAfterSaveProcess(BeforeAndAfterSaveProcessTests.AfterSaveProcess);
			ReadWriteServiceTestModelSimple.Save(testModel);

			Assert.Equal(AFTER_AFTER_PROCESS_STRING, testModel.TestField);
		}

		[Fact]
		public void ASaveResultOfSavedWillBeReturnByTheSaveMethod()
		{
			var testModel = new TestModelSimple() { TestField = "TestData" };

			ReadWriteService<TestModelSimple>.AddAfterSaveProcess(BeforeAndAfterSaveProcessTests.AfterSaveProcess);
			var response = ReadWriteServiceTestModelSimple.Save(testModel);

			Assert.Equal(SaveResult.Saved, response);
		}
	}
}
